package logic.impl;

import java.io.File;
import java.io.FileInputStream;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Properties;
import java.util.Vector;

import util.LevenshteinDistance;

import logic.impl.ResolverRegionFromPostalCode.CountryPostalCodeInfo;
import model.AddressWrapper;
import model.PhoneNumberWrapper;
import model.VCardWrapper;

/**
 * 
 * resolver:
 * 
 * - if country code of phone / fax is missing, try to resolve it using the address
 * - if country of field address is missing, try to resolve it using the country code of phone / fax
 *
 */
public class ResolverCityTelecomPrefix extends LogicElementBaseClass {
	
	class CityPrefixInfo {
		public Properties mapping;
		public Properties mappingInverted;
	}
	
	private Hashtable<String, CityPrefixInfo>cityMappings;
	private int maxLevenshteinDistanceForCity;
	
	@Override
	public void init(Properties resolverProperties) {
		super.init(resolverProperties);
		
		cityMappings = new Hashtable<String, CityPrefixInfo>();
		maxLevenshteinDistanceForCity = 2;

		if(resolverProperties.containsKey("ResolverCityTelecomPrefix.PathMappingPrefix")) {
			try {
					String path = resolverProperties.getProperty("ResolverCityTelecomPrefix.PathMappingPrefix");
					log.info("loading mapping city->telecomPrefix from:" + path);
									
					File dir = new File(path);
					File[] mappingList = dir.listFiles();
					
					/**
					 * search given directory for mapping files
					 */
					if(mappingList != null) {
						for(int i = 0; i < mappingList.length; i++) {
							File myMappingFile = mappingList[i];
							if(myMappingFile.isDirectory() || myMappingFile.canRead() == false) continue;
							
							Properties props = new Properties();
							props.load(new FileInputStream(myMappingFile));
							
							String countryName = props.getProperty("countryName").toLowerCase();
							log.info("loading city->telecomPrefix mapping file for country: " + countryName +
									" from: " + myMappingFile.getAbsolutePath());
							
							CityPrefixInfo cMapping = null;
							cMapping = cityMappings.get(countryName);
							if(cMapping == null) cMapping = new CityPrefixInfo();
							
							props.remove("countryName");
							
							Properties propsLowerCase = new Properties();
							Iterator iter = props.keySet().iterator();
							while(iter.hasNext()) {
								String key = (String) iter.next();
								propsLowerCase.put(key.toLowerCase(), props.get(key));
							}
							
							cMapping.mapping = propsLowerCase;
							cityMappings.put(countryName, cMapping);
							
						}
					}
			} catch (Exception exc) {
				log.error(exc);
				exc.printStackTrace();
			}		

		}
		
		if(resolverProperties.containsKey("ResolverCityTelecomPrefix.PathMappingPrefixInv")) {
			try {
				String path = resolverProperties.getProperty("ResolverCityTelecomPrefix.PathMappingPrefixInv");
				log.info("loading mapping telecomPrefix->city from:" + path);
								
				File dir = new File(path);
				File[] mappingList = dir.listFiles();
				
				/**
				 * search given directory for mapping files
				 */
				if(mappingList != null) {
					for(int i = 0; i < mappingList.length; i++) {
						File myMappingFile = mappingList[i];
						if(myMappingFile.isDirectory() || myMappingFile.canRead() == false) continue;
						
						Properties props = new Properties();
						props.load(new FileInputStream(myMappingFile));
						
						String countryName = props.getProperty("countryName").toLowerCase();
						log.info("loading telecomPrefix->city mapping file for country: " + countryName +
								" from: " + myMappingFile.getAbsolutePath());
						
						CityPrefixInfo cMapping = null;
						cMapping = cityMappings.get(countryName);
						if(cMapping == null) cMapping = new CityPrefixInfo();
						
						props.remove("countryName");

						cMapping.mappingInverted = props;
						cityMappings.put(countryName, cMapping);
						
					}
				}
			} catch (Exception exc) {
				log.error(exc);
				exc.printStackTrace();
			}			
		}		
		
		if(resolverProperties.containsKey("ResolverCityTelecomPrefix.MAXLDist_City")) {
			try {
				maxLevenshteinDistanceForCity = new Integer(
						resolverProperties.getProperty(
								"ResolverCityTelecomPrefix.MAXLDist_City"))
				.intValue();
				log.info("set maxLevenshteinDistanceForCity to:" + maxLevenshteinDistanceForCity);
			} catch (Exception exc) {
				log.error(exc);
			}
		}				
		
	}

	public void performResolving() {

		Vector <VCardWrapper>addressCards = this.getAddressBook().getAddressCards();
		for(int i = 0; i < addressCards.size(); i++) {
			VCardWrapper myCard = addressCards.get(i);
			
			if(myCard.existsAddress() && myCard.getAddress().existsCountryName() &&
					(myCard.existsPhone() || myCard.existsFax())) {
				
				AddressWrapper address = myCard.getAddress();
				PhoneNumberWrapper phone = null;
				
				if(myCard.existsPhone())
					phone = myCard.getPhone();
				else
					phone = myCard.getFax();
				
				if(address.existsLocality() == false) {
					/**
					 * resolve locality of address field from phone / fax
					 */
					performResolvingLocalityfromPhone(address, phone);
				} else {
					if(phone.existsAreaCode() == false) {
						/**
						 * resolve area code of phone / fax from locality of address field
						 */
						performResolvingPhonefromLocality(address, phone);
					}	
				}
			}
			
			if(myCard.existsPhone() && myCard.existsFax()) {
				boolean phonePrefixexists = myCard.getPhone().existsAreaCode();
				boolean faxPrefixexists = myCard.getFax().existsAreaCode();
				
				if(phonePrefixexists && faxPrefixexists == false)
					myCard.getFax().setAreaCode(
							myCard.getPhone().getAreaCode());
				
				if(phonePrefixexists == false && faxPrefixexists)
					myCard.getPhone().setAreaCode(
							myCard.getFax().getAreaCode());
				
			}
		}

		
	}	
	
	/**
	 * 
	 * @param address address to be completed
	 * @param phone number whose area code is used as input for resolving
	 */
	public void performResolvingLocalityfromPhone(AddressWrapper address, PhoneNumberWrapper phone) {
				
		CityPrefixInfo mapping = cityMappings.get(address.getCountryName().toLowerCase());
		if(mapping == null || mapping.mappingInverted == null) return;
		
		String areaCode = phone.getAreaCode();
		String locality = null;

		if(mapping.mappingInverted.containsKey(areaCode)) {
			locality = (String) mapping.mappingInverted.getProperty(areaCode);
			address.setLocality(locality);
			
			log.info("++++++++++ resolved locality from phoneAreaCode: " + areaCode + " -> " + locality + " ++++++++++");
		}
		
	}
	
	/**
	 * 
	 * @param address address whose locality name is used as input for resolving
	 * @param phone number to be completed
	 */
	public void performResolvingPhonefromLocality(AddressWrapper address, PhoneNumberWrapper phone) {
		try {
		CityPrefixInfo mapping = cityMappings.get(address.getCountryName().toLowerCase());
		if(mapping == null || mapping.mapping == null) return;
		
		String locality = address.getLocality().toLowerCase();
		String areaCode = null;

		if(mapping.mapping.containsKey(locality)) {
			areaCode = (String) mapping.mapping.getProperty(locality);
			phone.setAreaCode(areaCode);
			
			log.info("++++++++++ resolved phoneAreaCode from Locality: " + locality + " -> " + areaCode + " ++++++++++");
		} else {
			log.info("-------------------- searching for:" + locality);
			/**
			 * locality wasn't found in mapping table -> maybe a misspelled locality name
			 */
			Iterator localityIter = mapping.mapping.keySet().iterator();
			while(localityIter.hasNext()) {
				
				String cEntry = (String) localityIter.next();
				if(LevenshteinDistance.LD(locality, cEntry) <= this.maxLevenshteinDistanceForCity) {
					
					/**
					 * levenshtein distance is small enough for a match
					 */
					areaCode = (String) mapping.mapping.getProperty(cEntry);
					address.setLocality(cEntry);
					phone.setAreaCode(areaCode);
			
					log.info("++++++++++ resolved phoneAreaCode from locality: (" + locality + " -> " + cEntry
							+ ") -> " + areaCode + " ++++++++++");
				}
				
			}
			
		}
		} catch (Exception exc) {
			exc.printStackTrace();
		}
	}
	
}